﻿using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Core.Filters;
using LightCAD.Runtime;
using SkiaSharp;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using LightCAD.MathLib;
using System.Text.RegularExpressions;

namespace LightCAD.Drawing.Actions
{
    public class GroupAction : ElementAction
    {
        public static string CommandName;
        public static LcCreateMethod[] CreateMethods;

        static GroupAction()
        {
            CreateMethods = new LcCreateMethod[4];
            CreateMethods[0] = new LcCreateMethod()
            {
                Name = "Create",
                Description = "创建组",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name=  "Step0", Options= "选择对象或[名称(N) 说明(D)]:" },
                    new LcCreateStep { Name=  "Step1", Options= "输入编组名或[?]:" },
                    new LcCreateStep { Name=  "Step2", Options= "输入组说明:" },
                }
            };
            CreateMethods[1] = new LcCreateMethod()
            {
                Name = "Edit",
                Description = "编辑组",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name = "Step0", Options = "选择组或[名称(N)]:" },
                    new LcCreateStep { Name = "Step1", Options = "输入编组名或[?]:" },
                    new LcCreateStep { Name = "Step2", Options = "输入选项[添加对象(A) 删除对象(R) 重命名(REN)]:" },
                    new LcCreateStep { Name = "Step3", Options = "输入组的新名称或[?]:" },
                    new LcCreateStep { Name = "Step4", Options = "选择要添加的对象:" },
                    new LcCreateStep { Name = "Step5", Options = "选择要删除的对象:" },
                    new LcCreateStep { Name = "Step6", Options = "对象是多个组的成员[所有(A) 选择(S)]" },
                }
            };
            CreateMethods[2] = new LcCreateMethod()
            {
                Name = "Ungroup",
                Description = "解除组",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name = "Step0", Options = "选择组或[名称(N)]:" },
                    new LcCreateStep { Name = "Step1", Options = "输入编组名或[?]:" },
                }
            };
            CreateMethods[3] = new LcCreateMethod()
            {
                Name = "Plckgroup",
                Description = "编辑组",
                Steps = new LcCreateStep[]
                {
                    new LcCreateStep { Name = "Step0", Options = "进入组编辑状态(S)结束组编辑状态（E）"},

                }
            };
        }

        internal static void Initilize()
        {
            ElementActions.Group = new GroupAction();
            LcDocument.ElementActions.Add(BuiltinElementType.Group, ElementActions.Group);
        }
        private InElementsInputer InElementsInputer { get; set; }
        private ElementSetInputer ElementInputers { get; set; }
        private ElementInputer ElementInputer { get; set; }
        private CmdTextInputer CmdTextInputer { get; set; }
        private GroupAction() { }
        public GroupAction(IDocumentEditor docEditor) : base(docEditor)
        {
            this.commandCtrl.WriteInfo("命令：Group");
        }

        public async void ExecCreate(string[] args = null)
        {
            if (CreateSelects(this.docRt.Action.SelectedElements)) return;
            this.StartCreating();
            var curMethod = CreateMethods[0];
            this.ElementInputers = new ElementSetInputer(this.docEditor);
            this.CmdTextInputer = new CmdTextInputer(this.docEditor);
            string name = "";
            string description = "";
            Step0:
            List<LcElement> InputElements = new List<LcElement>();
            var step0 = curMethod.Steps[0];
            var result0 = await ElementInputers.Execute(step0.Options);
            if (ElementInputers.isCancelled) { this.Cancel(); return; }
            foreach (var item in (List<LcElement>)result0.ValueX)
            {
                if (!InputElements.Contains(item))
                {
                    InputElements.Add(item);
                }
            }
            if (result0.Option != null)
            {
                if (result0.Option.ToUpper() == "N")
                {
                    goto Step1;
                }
                else if (result0.Option.ToUpper() == "D")
                {
                    goto Step2;
                }
                if (result0.Option != null && result0.Option.Length > 0)
                {
                    goto Step0;
                }
                else
                    goto End;
            }

            Step1:
            var step1 = curMethod.Steps[1];
            var result1 = await CmdTextInputer.Execute(step1.Options);
            if (CmdTextInputer.isCancelled)
            {
                this.Cancel();
                return;
            }

            name = result1.ValueX;
            goto Step0;
            Step2:
            var step2 = curMethod.Steps[2];
            var result2 = await CmdTextInputer.Execute(step2.Options);
            if (ElementInputers.isCancelled)
            {
                this.Cancel();
                return;
            }

            description = result2.ValueX;
            if (InputElements.Count > 0)
            {
                goto End;
            }
            goto Step0;

            End:
            CreateSelects(InputElements, name, description);
            this.EndCreating();
        }

        private bool CreateSelects(List<LcElement> elements, string name = "", string description = "")
        {

            var doc = this.docRt.Document;
            if (elements.Count > 0)
            {
                DocumentManager.CurrentRecorder.BeginAction("GROUP");
                var group = doc.CreateObject<LcGroup>();
                group.IsPlck = false;
                group.Name = name;
                group.Description = description;
                group.eleIds = new List<long>();
                foreach (var element in elements)
                {
                    doc.ModelSpace.RemoveElement(element);
                   
     
                    group.eleIds.Add(element.Id);
                    group.InsertElement(element);
                } 
                doc.ModelSpace.InsertElement(group);
                this.docRt.Action.ClearSelects();
                DocumentManager.CurrentRecorder.EndAction();
                return true;
            }
            return false;

        }

        public async void ExecEdit(string[] args = null)
        {
            this.StartCreating();
            var curMethod = CreateMethods[1];
            List<LcElement> groups = new List<LcElement>();
            groups = GetGroupSelects();
            Step:
            this.docRt.Action.SelectElements(this.docEditor, groups);
            if (groups.Count > 0)
            {
                if (groups.Count == 1)
                {
                    goto Step2;
                }
                else
                {
                    goto Step6;
                }
            }
            else
            {
                goto Step0;
            }
            Step0:
            var step0 = curMethod.Steps[0];
            this.ElementInputer = new ElementInputer(this.docEditor);
            var result0 = await ElementInputer.Execute(step0.Options);
            if (ElementInputer.isCancelled) { this.Cancel(); return; }
            if (result0.Option != null && result0.Option.ToUpper() == "N")
            {
                goto Step1;
            }
            else
            {
                groups = new List<LcElement>();
                LcElement element = (LcElement)result0.ValueX;
                this.docRt.Action.ClearSelects();
                if (element != null && element.Type == BuiltinElementType.Group)
                {
                    groups.Add(element);
                }
                goto Step;
            }
            Step1:
            var step1 = curMethod.Steps[1];
            this.CmdTextInputer = new CmdTextInputer(this.docEditor);
            var result1 = await CmdTextInputer.Execute(step1.Options);
            if (CmdTextInputer.isCancelled) { this.Cancel(); return; }
            SimpleFilter simpleFilter = new SimpleFilter(this.docRt.Document.ModelSpace);
            groups = simpleFilter.GetGroup<LcElement>(result1.ValueX);
            goto Step;

            Step2:
            var step2 = curMethod.Steps[2];
            this.CmdTextInputer = new CmdTextInputer(this.docEditor);
            var result2 = await CmdTextInputer.Execute(step2.Options);
            if (CmdTextInputer.isCancelled) { this.Cancel(); return; }
            if (result2.ValueX == "A")
            {
                goto Step4;
            }
            else
            if (result2.ValueX == "R")
            {
                goto Step5;
            }
            else
            if (result2.ValueX == "REN")
            {
                goto Step3;
            }
            else
            {
                goto Step2;
            }
            Step3:
            var step3 = curMethod.Steps[3];
            this.CmdTextInputer = new CmdTextInputer(this.docEditor);
            string Options = step3.Options;
            if ((groups.FirstOrDefault() as LcGroup).Name != null && (groups.FirstOrDefault() as LcGroup).Name.Trim().Length > 0)
            {
                Options += "<" + (groups.FirstOrDefault() as LcGroup).Name + ">";
            }
            var result3 = await CmdTextInputer.Execute(Options);
            if (CmdTextInputer.isCancelled) { this.Cancel(); return; }
            RenameGroups(groups, result3.ValueX);
            goto End;
            Step4:
            var step4 = curMethod.Steps[4];
            this.ElementInputers = new ElementSetInputer(this.docEditor);
            var result4 = await ElementInputers.Execute(step4.Options);
            if (ElementInputers.isCancelled) { this.Cancel(); return; }
            AddGroupSelects(groups.FirstOrDefault(), (List<LcElement>)result4.ValueX);
            goto End;
            Step5:
            List<LcElement> removeElements = new List<LcElement>();
            var step5 = curMethod.Steps[5];
            this.InElementsInputer = new InElementsInputer(this.docEditor, (groups.FirstOrDefault() as LcGroup).Elements);
            InputResult result5 = new InputResult();
            do
            {
                result5 = await InElementsInputer.Execute(step5.Options);
                if (InElementsInputer.isCancelled) { this.Cancel(); return; }
                LcElement element = result5.ValueX as LcElement;
                if (!removeElements.Contains(element))
                {
                    removeElements.Add(element);
                    element.IsSelected = false;
                }

            } while (result5.Option == null);

            RemoveGroupSelects(groups.FirstOrDefault(), removeElements);
            goto End;
            Step6:
            End:
            this.EndCreating();
            this.docRt.Action.ClearSelects();

        }
        public async void ExecPlckEdit(string[] args = null)
        {
            this.StartCreating();
            var curMethod = CreateMethods[3];
            List<LcElement> groups = new List<LcElement>();
            Step0:
            var step0 = curMethod.Steps[0];
            this.CmdTextInputer = new CmdTextInputer(this.docEditor);
            var result2 = await CmdTextInputer.Execute(step0.Options);
            if (CmdTextInputer.isCancelled) { this.Cancel(); return; }
            if (result2.ValueX.ToUpper() == "S")
            {
                groups = GetGroupSelects();
                foreach (var item in groups)
                {
                    LcGroup group = item as LcGroup;
                    group.IsPlck = true;
                    PlckGroupSelects(groups.FirstOrDefault(), group.Elements.ToList());
                }

            }
            else
            if (result2.ValueX.ToUpper() == "E")
            {

                var elegroups = this.docRt.Document.ModelSpace.Elements.Where((ele) => ele.Type == BuiltinElementType.Group || ele.Type == BuiltinElementType.AxisGrid).ToList();
                foreach (var item in elegroups)
                {
                    if ((item as LcGroup).IsPlck)
                        PlckAddGroup(item as LcGroup);
                }

            }


            End:
            this.EndCreating();
            this.docRt.Action.ClearSelects();

        }
        public async void ExecUngroup(string[] args = null)
        {
            if (UngroupSelects(this.docRt.Action.SelectedElements)) return;
            this.StartCreating();
            var curMethod = CreateMethods[2];
            Step0:
            var step0 = curMethod.Steps[0];
            this.ElementInputers = new ElementSetInputer(this.docEditor);
            var result0 = await ElementInputers.Execute(step0.Options);
            if (ElementInputers.isCancelled) { this.Cancel(); return; }
            if (result0.Option != null && result0.Option.ToUpper() == "N")
            {
                goto Step1;
            }
            else
            {

                List<LcElement> elements = (List<LcElement>)result0.ValueX;
                if (elements.Count == 0)
                {
                    goto Step0;
                }
                if (UngroupSelects(elements))
                {
                    this.docRt.Action.ClearSelects();
                    goto End;
                }

            }
            Step1:
            var step1 = curMethod.Steps[1];
            this.CmdTextInputer = new CmdTextInputer(this.docEditor);
            var result1 = await CmdTextInputer.Execute(step1.Options);
            if (CmdTextInputer.isCancelled) { this.Cancel(); return; }
            SimpleFilter simpleFilter = new SimpleFilter(this.docRt.Document.ModelSpace);

            if (UngroupSelects(simpleFilter.GetGroup<LcElement>(result1.ValueX)))
            {

                goto End;
            }
            End:
            this.EndCreating();
            this.docRt.Action.ClearSelects();
        }
        private bool UngroupSelects(List<LcElement> elements)
        {
            var groups = this.docRt.Action.SelectedElements.FindAll((ele) => ele.Type == BuiltinElementType.Group);
            if (groups.Count > 0)
            {
                var doc = this.docRt.Document;
                this.docRt.Action.ClearSelects();//这里需要先清除选择，否则解组后无法清除
                foreach (LcGroup group in groups)
                {
                    for (var i = group.Elements.Count - 1; i >= 0; i--)
                    {
                        var element = group.Elements[i];
                        group.RemoveElement(element);
                        (group as LcGroup).eleIds.Remove(element.Id);
                        doc.ModelSpace.InsertElement(element);
                    }
                    doc.ModelSpace.RemoveElement(group);
                }
                return true;
            }
            return false;
        }
        private List<LcElement> GetGroupSelects()
        {
            var elements = this.docRt.Action.SelectedElements.FindAll((ele) => ele.Type == BuiltinElementType.Group);
            elements.AddRange( this.docRt.Action.SelectedElements.FindAll((ele) => ele.Type == BuiltinElementType.AxisGrid));
            this.docRt.Action.ClearSelects();
            return elements;

        }
        private void RemoveGroupSelects(LcElement group, List<LcElement> elements)
        {
            DocumentManager.CurrentRecorder.BeginAction("GROUP");
            var doc = this.docRt.Document;
            foreach (LcElement item in elements)
            {
                if (group != item)
                {
                    (group as LcGroup).RemoveElement(item);
                    (group as LcGroup).eleIds.Remove(item.Id);
                    doc.ModelSpace.InsertElement(item);
                }

            }
            if ((group as LcGroup).Elements.Count == 0)
            {
                doc.ModelSpace.RemoveElement(group);
            }
            this.docRt.Action.ClearSelects();
            DocumentManager.CurrentRecorder.EndAction();
        }
        private void PlckGroupSelects(LcElement group, List<LcElement> elements)
        {
            DocumentManager.CurrentRecorder.BeginAction("PlckGROUP");
            var doc = this.docRt.Document;
            foreach (LcElement item in elements)
            {
                if (group != item)
                {
                    (group as LcGroup).RemoveElement(item);
         
                    doc.ModelSpace.InsertElement(item);
                }

            }

            this.docRt.Action.ClearSelects();
            DocumentManager.CurrentRecorder.EndAction();
        }
        private void PlckAddGroup(LcGroup group)
        {
            DocumentManager.CurrentRecorder.BeginAction("GROUP");
            var doc = this.docRt.Document;
            List<LcElement> elements = this.docRt.Document.ModelSpace.Elements.Where((ele) => group.eleIds.Contains(ele.Id)).ToList();
            foreach (LcElement item in elements)
            {

                if (group != item)
                {
                    doc.ModelSpace.RemoveElement(item);
                    (group as LcGroup).InsertElement(item);
                    (group as LcGroup).IsPlck = false;
                }


            }
            this.docRt.Action.ClearSelects();
            DocumentManager.CurrentRecorder.EndAction();
        }
        private void AddGroupSelects(LcElement group, List<LcElement> elements)
        {
            DocumentManager.CurrentRecorder.BeginAction("GROUP");
            var doc = this.docRt.Document;
            foreach (LcElement item in elements)
            {
                if (group != item)
                {
                    doc.ModelSpace.RemoveElement(item);
                    (group as LcGroup).InsertElement(item);
                    (group as LcGroup).eleIds.Add(item.Id);
                }

            }
            this.docRt.Action.ClearSelects();
            DocumentManager.CurrentRecorder.EndAction();
        }
        private bool RenameGroups(List<LcElement> Groups, string name)
        {
            DocumentManager.CurrentRecorder.BeginAction("GROUP");
            foreach (var group in Groups)
            {
                (group as LcGroup).Name = name;
            }
            DocumentManager.CurrentRecorder.EndAction();
            return true;

        }
        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
            //TODO:
            this.EndCreating();
        }
        public override void Draw(SKCanvas canvas, LcElement element, Vector2 offset)
        {
            var grp = element as LcGroup;
            foreach (var ele in grp.Elements)
            {
                var eleAction = (ele.RtAction as ElementAction);

                eleAction.SetViewport(this.vportRt).Draw(canvas, ele, offset);

            }
        }
        public override void CreateElement(LcElement element, Matrix3 matrix)
        {
            var grp = element as LcGroup;
            foreach (var ele in grp.Elements)
            {
                var eleAction = (ele.RtAction as ElementAction);
                eleAction.CreateElement(ele, matrix);
            }
        }
        public override void CreateElement(LcElement element, Vector2 basePoint, double scaleFactor)
        {
            var grp = element as LcGroup;
            foreach (var ele in grp.Elements)
            {
                var eleAction = (ele.RtAction as ElementAction);
                eleAction.CreateElement(ele, basePoint, scaleFactor);
            }

        }
        public override void CreateElement(LcElement element, Vector2 basePoint, Vector2 scaleVector)
        {
            var grp = element as LcGroup;
            foreach (var ele in grp.Elements)
            {
                var eleAction = (ele.RtAction as ElementAction);
                eleAction.CreateElement(ele, basePoint, scaleVector);
            }

        }
        public override void Draw(SKCanvas canvas, LcElement element, Matrix3 matrix)
        {
            var grp = element as LcGroup;
            foreach (var ele in grp.Elements)
            {
                var eleAction = (ele.RtAction as ElementAction);

                eleAction.Draw(canvas, ele, matrix);

            }
        }

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var group = element as LcGroup;
            var grips = new List<ControlGrip>();
            var gripCenter = new ControlGrip
            {
                Element = group,
                Name = "Center",
                Position = group.BoundingBox.Center
            };
            grips.Add(gripCenter);
            return grips.ToArray();
        }
        private string _gripName;
        private Vector2 _position;
        private Vector2 _endDrag;
        private LcGroup _group;

        public override void SetDragGrip(LcElement element, string gripName, Vector2 position, bool isEnd)
        {
            _group = element as LcGroup;

            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
            }
            else
            {
                if (gripName == "Center")
                {
                    var delta = position - _group.BoundingBox.Center;
                    _group.Translate(delta);
                }
            }
        }


        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_group == null) return;

            var offset = _position - _group.BoundingBox.Center;
            foreach (var ele in _group.Elements)
            {
                (ele.RtAction as ElementAction).Draw(canvas, ele, offset);
            }
        }

    }
}
